import { Segment, Vec2 } from "./geometry";

export enum IntrType {
  NONE,
  INTR,
  ALONG,
}

export class IntrResult {
  type: IntrType;
  count: number = 0;
  points: Vec2[] = [];

  constructor(type: IntrType = IntrType.NONE) {
    this.type = type;
  }

  static readonly NOINTR: IntrResult = new IntrResult(IntrType.NONE);
}

export class Intr {
  // static
  static intr2Segments(seg1: Segment, seg2: Segment): IntrResult {
    return Intr.intr2SegmentsRaw(seg1.p1, seg1.p2, seg2.p1, seg2.p2);
  }

  static intr2SegmentsRaw(p1: Vec2, p2: Vec2, p3: Vec2, p4: Vec2) {
    // 跨立测试1
    let s1 = p2.sub(p1);
    let v1 = p3.sub(p1);
    let v2 = p4.sub(p1);
    let a1 = s1.cross(v1);
    let a2 = s1.cross(v2);
    if (a1 * a2 > 0) {
      return IntrResult.NOINTR;
    }

    // 跨立测试2
    let s2 = p4.sub(p3);
    let v3 = p1.sub(p3);
    let v4 = p2.sub(p3);
    let a3 = s2.cross(v3);
    let a4 = s2.cross(v4);
    if (a3 * a4 > 0) {
      return IntrResult.NOINTR;
    }

    // 共线测试
    let a5 = s2.cross(s1);
    if (a5 === 0) {
      let dx1 = p2.x - p1.x;
      let dx2 = p4.x - p3.x;
      if (dx1 === 0 || dx2 === 0) {
        throw `segment degenerate to a point!!!`;
      }

      let t1 = (p3.x - p1.x) / dx1;
      let t2 = (p4.x - p1.x) / dx1;
      if ((t1 < 0 && t2 < 0) || (t1 > 1 && t2 > 1)) {
        return IntrResult.NOINTR;
      }

      let ret = new IntrResult(IntrType.ALONG);
      if (
        (t1 === 0 && t2 < 0) ||
        (t2 === 0 && t1 < 0) ||
        (t1 === 1 && t2 > 1) ||
        (t2 === 1 && t1 > 1)
      ) {
        ret.count = 1;
        ret.points.push(t1 === 0 || t2 === 0 ? p1 : p2);
        return ret;
      }

      ret.count = 2;
      ret.points.push(Vec2.lerp(t1, p1, p2));
      ret.points.push(Vec2.lerp(t2, p1, p2));
      return ret;
    }

    let ret = new IntrResult(IntrType.INTR);
    ret.count = 1;
    let t = Math.abs(a1) / (Math.abs(a1) + Math.abs(a2));
    ret.points.push(Vec2.lerp(t, p3, p4));
    return ret;
  }
}
